using System;
using System.Linq;
using JetBrains.Annotations;
using Kaleida.ServiceMonitor.Framework;

namespace Kaleida.ServiceMonitor.Model.Runtime
{
    public class MonitorState : IMonitorState
    {
        private readonly Monitor monitor;
        private bool isMonitoring;
		private readonly IStatePresentation statePresentation;
		private readonly LimitedList<RequestResult> recentResponses;
		private readonly LimitedList<RequestResult> recentFailures; //TODO: use more appropriate list item type RequestErrorResult
        public const int MaximumStoredFailureCount = 100;

        public MonitorState(Monitor monitor, int maximumResponses)
        {
            this.monitor = monitor;
            statePresentation = new DefaultStatePresentation(this);
			recentResponses = new LimitedList<RequestResult>("Response", maximumResponses);
            recentFailures = new LimitedList<RequestResult>("Failure", MaximumStoredFailureCount, monitor.CompiledModule.MaximumErrorAge);
        }

        internal event EventHandler MonitorStarted;
        internal event EventHandler MonitorStopped;

        public Monitor Monitor
        {
            get { return monitor; }
        }

        public IStatePresentation DefaultPresentation
        {
            get { return statePresentation; }
        }

		public LimitedList<RequestResult> RecentResponses
		{
			get { return recentResponses; }
		}

		public LimitedList<RequestResult> RecentFailures
		{
			get { return recentFailures; }
		}

        public int FailureCount
        {
			get { return recentFailures.ItemCount; }
        }

        [CanBeNull]
        public Percentage SuccessRate
        {
            get
            {
                var successRate = monitor.Statistics.Combined.GetSuccessRate();
                return successRate == null ? null : Percentage.FromDecimal(successRate.Value);
            }
        }

        public bool IsMonitoring
        {
            get { return isMonitoring; }
        }

        public bool CanBeginMonitoring
        {
            get { return !isMonitoring && monitor.CompiledModule.Operations.Any(); }
        }

        public bool HasFailures
        {
			get { return recentFailures.ItemCount != 0; }
        }

        [CanBeNull]
        public RequestErrorResult FirstFailureOrDefault
        {
			get{ return (RequestErrorResult) recentFailures.OldestItemOrDefault; }
        }

        [CanBeNull]
        public RequestErrorResult LatestFailureOrDefault
        {
            get { return (RequestErrorResult)recentFailures.NewestItemOrDefault; }
        }

        public void ClearFailures()
        {
			recentFailures.Clear();
        }

        public void ScriptCompiled(ScriptCompilation compilation)
        {
            isMonitoring = isMonitoring && compilation.Succeeded;
        }

        public void StartMonitoring()
        {
            if(!CanBeginMonitoring)
                throw new InvalidOperationException("Monitor is not in a suitable state to start");

            isMonitoring = true;

            if (MonitorStarted != null)
                MonitorStarted(this, EventArgs.Empty);
        }

        public void StopMonitoring()
        {
            if (!IsMonitoring)
                throw new InvalidOperationException("Monitor is already started");

            isMonitoring = false;

            if (MonitorStopped != null)
                MonitorStopped(this, EventArgs.Empty);
        }

		internal void AddResult(RequestSuccessResult result)
		{
			recentResponses.AddToTop(result);
		}

		internal void AddResult(RequestErrorResult result)
		{
			recentResponses.AddToTop(result);
			recentFailures.AddToTop(result);
		}

    }
}